package io.legado.app.ui.login

import android.content.DialogInterface
import android.os.Bundle
import android.text.InputType
import android.view.View
import android.view.ViewGroup
import androidx.core.view.setPadding
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import io.legado.app.R
import io.legado.app.base.BaseDialogFragment
import io.legado.app.constant.AppLog
import io.legado.app.data.entities.BaseSource
import io.legado.app.data.entities.rule.RowUi
import io.legado.app.databinding.DialogLoginBinding
import io.legado.app.databinding.ItemFilletTextBinding
import io.legado.app.databinding.ItemSourceEditBinding
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.theme.primaryColor
import io.legado.app.utils.GSON
import io.legado.app.utils.applyTint
import io.legado.app.utils.dpToPx
import io.legado.app.utils.isAbsUrl
import io.legado.app.utils.openUrl
import io.legado.app.utils.printOnDebug
import io.legado.app.utils.setLayout
import io.legado.app.utils.showDialogFragment
import io.legado.app.utils.toastOnUi
import io.legado.app.utils.viewbindingdelegate.viewBinding
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import splitties.views.onClick
import kotlin.collections.HashMap
import kotlin.collections.List
import kotlin.collections.forEachIndexed
import kotlin.collections.hashMapOf
import kotlin.collections.set


class SourceLoginDialog : BaseDialogFragment(R.layout.dialog_login, true) {

    private val binding by viewBinding(DialogLoginBinding::bind)
    private val viewModel by activityViewModels<SourceLoginViewModel>()

    /**
     * 在活动或片段启动时调用的方法
     *
     * 该方法重写了从 Activity 或 Fragment 类继承来的方法，主要用于在 UI 组件可见性发生变化时进行处理
     * 在此方法中，我们设置了布局的宽度和高度参数，以确保其充满整个父容器
     */
    override fun onStart() {
        super.onStart()
        // 设置布局参数，使其充满父容器
        setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
    }

    /**
     * 在片段创建时调用，用于初始化登录界面
     *
     * @param view 生成的视图
     * @param savedInstanceState 保存的实例状态，如果有的话
     */
    override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
        // 获取登录数据源，如果没有则直接返回
        val source = viewModel.source ?: return

        // 设置工具栏背景色和标题
        binding.toolBar.setBackgroundColor(primaryColor)
        binding.toolBar.title = getString(R.string.login_source, source.getTag())

        // 获取登录信息映射和UI配置
        val loginInfo = source.getLoginInfoMap()
        val loginUi = source.loginUi()

        // 遍历登录UI配置，动态生成登录表单
        loginUi?.forEachIndexed { index, rowUi ->
            when (rowUi.type) {
                RowUi.Type.text -> ItemSourceEditBinding.inflate(
                    layoutInflater,
                    binding.root,
                    false
                ).let {
                    binding.flexbox.addView(it.root)
                    it.root.id = index + 1000
                    it.textInputLayout.hint = rowUi.name
                    it.editText.setText(loginInfo?.get(rowUi.name))
                }
                RowUi.Type.password -> ItemSourceEditBinding.inflate(
                    layoutInflater,
                    binding.root,
                    false
                ).let {
                    binding.flexbox.addView(it.root)
                    it.root.id = index + 1000
                    it.textInputLayout.hint = rowUi.name
                    it.editText.inputType =
                        InputType.TYPE_TEXT_VARIATION_PASSWORD or InputType.TYPE_CLASS_TEXT
                    it.editText.setText(loginInfo?.get(rowUi.name))
                }
                RowUi.Type.button -> ItemFilletTextBinding.inflate(
                    layoutInflater,
                    binding.root,
                    false
                ).let {
                    binding.flexbox.addView(it.root)
                    it.root.id = index + 1000
                    it.textView.text = rowUi.name
                    it.textView.setPadding(16.dpToPx())
                    it.root.onClick {
                        Coroutine.async {
                            if (rowUi.action.isAbsUrl()) {
                                context?.openUrl(rowUi.action!!)
                            } else {
                                // 执行JavaScript操作
                                rowUi.action?.let { buttonFunctionJS ->
                                    kotlin.runCatching {
                                        source.getLoginJs()?.let { loginJS ->
                                            source.evalJS("$loginJS\n$buttonFunctionJS") {
                                                put("result", getLoginData(loginUi))
                                            }
                                        }
                                    }.onFailure { e ->
                                        AppLog.put(
                                            "LoginUI Button ${rowUi.name} JavaScript error",
                                            e
                                        )
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        // 初始化工具栏菜单并设置点击监听器
        binding.toolBar.inflateMenu(R.menu.source_login)
        binding.toolBar.menu.applyTint(requireContext())
        binding.toolBar.setOnMenuItemClickListener { item ->
            when (item.itemId) {
                R.id.menu_ok -> {
                    val loginData = getLoginData(loginUi)
                    login(source, loginData)
                }
                R.id.menu_show_login_header -> alert {
                    setTitle(R.string.login_header)
                    source.getLoginHeader()?.let { loginHeader ->
                        setMessage(loginHeader)
                    }
                }
                R.id.menu_del_login_header -> source.removeLoginHeader()
            }
            return@setOnMenuItemClickListener true
        }
    }

    /**
     * 从登录界面的UI列表中提取登录数据
     *
     * 此函数遍历登录界面的UI列表，寻找类型为"text"或"password"的行，
     * 并从这些行中提取用户输入的数据，将其存储在一个HashMap中
     *
     * @param loginUi 登录界面的UI列表，可能为null如果为null，函数将返回一个空的HashMap
     * @return 包含登录数据的HashMap，键为UI行的名称，值为用户输入的文本
     */
    private fun getLoginData(loginUi: List<RowUi>?): HashMap<String, String> {
        // 初始化一个空的HashMap来存储登录数据
        val loginData = hashMapOf<String, String>()

        // 遍历登录UI列表，如果列表为空，则什么都不做
        loginUi?.forEachIndexed { index, rowUi ->
            // 当前行的类型为"text"或"password"时，尝试提取数据
            when (rowUi.type) {
                "text", "password" -> {
                    // 根据索引找到对应的视图组件
                    val rowView = binding.root.findViewById<View>(index + 1000)
                    // 使用ItemSourceEditBinding绑定视图组件，并尝试获取编辑框中的文本
                    ItemSourceEditBinding.bind(rowView).editText.text?.let {
                        // 如果编辑框中的文本不为空，则将其添加到登录数据中
                        loginData[rowUi.name] = it.toString()
                    }
                }
            }
        }
        // 返回提取到的登录数据
        return loginData
    }

    /**
     * 登录函数
     *
     * 该函数负责根据提供的登录信息进行用户登录操作如果登录信息为空，则会移除当前的登录信息并退出登录状态
     * 如果登录信息有效且登录成功，则会显示成功提示并退出登录状态
     * 在登录过程中，如果遇到异常，则会捕获并处理异常，并显示错误提示
     *
     * @param source 登录数据源，用于执行登录操作和管理登录信息
     * @param loginData 包含登录所需信息的哈希映射，键为登录信息的类型，值为对应的登录信息
     */
    private fun login(source: BaseSource, loginData: HashMap<String, String>) {
        lifecycleScope.launch(IO) {
            if (loginData.isEmpty()) {
                source.removeLoginInfo()
                withContext(Main) {
                    dismiss()
                }
            } else if (source.putLoginInfo(GSON.toJson(loginData))) {
                try {
                    source.login()
                    context?.toastOnUi(R.string.success)
                    withContext(Main) {
                        dismiss()
                    }
                } catch (e: Exception) {
                    AppLog.put("登录出错\n${e.localizedMessage}", e)
                    context?.toastOnUi("登录出错\n${e.localizedMessage}")
                    e.printOnDebug()
                }
            }
        }
    }

    override fun onDismiss(dialog: DialogInterface) {
        super.onDismiss(dialog)
        activity?.finish()
    }

}
